<template>
  <div class="relative inline-block">
    <div
      ref="textRef"
      class="text-content inline-block break-words"
      :style="{ maxHeight: isExpanded ? 'none' : maxHeight }"
    >
      <span>{{ isExpanded ? props.content : displayContent }}</span>
      <span v-if="!isExpanded && showEllipsis">...</span>
      <button
        v-if="showToggle"
        class="toggle-btn inline ml-1 px-1 text-primary"
        @click="toggleExpand"
      >
        {{ isExpanded ? "收起" : "展开" }}
      </button>
    </div>
  </div>
</template>

<script setup>
import { ref, onMounted, nextTick, watch } from "vue";

const props = defineProps({
  content: {
    type: String,
    required: true
  },
  lineClamp: {
    type: Number,
    default: 2
  },
  lineHeight: {
    type: String,
    default: "1.5"
  }
});

const emits = defineEmits(["expand", "collapse"]);

const textRef = ref(null);
const isExpanded = ref(false);
const showToggle = ref(false);
const showEllipsis = ref(false);
const displayContent = ref(props.content);
const maxHeight = ref("");

onMounted(() => {
  processText();
});

watch(
  () => props.content,
  () => {
    processText();
  }
);

watch(
  () => props.lineClamp,
  () => {
    processText();
  }
);

const processText = async () => {
  await nextTick();

  const textEl = textRef.value;
  if (!textEl) return;

  // 重置状态
  showToggle.value = false;
  showEllipsis.value = false;

  // 计算最大高度
  const computedLineHeight =
    parseFloat(getComputedStyle(textEl).lineHeight) ||
    parseFloat(props.lineHeight) * 16; // 默认16px字体大小
  maxHeight.value = `${computedLineHeight * props.lineClamp}px`;

  // 创建临时元素测量完整高度
  const tempEl = document.createElement("div");
  tempEl.style.visibility = "hidden";
  tempEl.style.position = "absolute";
  tempEl.style.width = `${textEl.offsetWidth}px`;
  tempEl.style.font = getComputedStyle(textEl).font;
  tempEl.style.lineHeight = getComputedStyle(textEl).lineHeight;
  tempEl.style.whiteSpace = "pre-wrap";
  tempEl.style.wordBreak = "break-word";
  tempEl.textContent = props.content;

  document.body.appendChild(tempEl);
  const fullHeight = tempEl.scrollHeight;
  document.body.removeChild(tempEl);

  // 检查是否需要截断
  if (fullHeight > parseFloat(maxHeight.value)) {
    showToggle.value = true;

    if (!isExpanded.value) {
      // 截断文本
      showEllipsis.value = true;
      truncateText();
    }
  } else {
    // 文本未超出限制，直接显示全文
    displayContent.value = props.content;
  }
};

const truncateText = () => {
  let text = props.content;
  let truncated = text;

  // 创建临时元素用于测量
  const tempEl = document.createElement("div");
  tempEl.style.visibility = "hidden";
  tempEl.style.position = "absolute";
  tempEl.style.width = `${textRef.value.offsetWidth}px`;
  tempEl.style.font = getComputedStyle(textRef.value).font;
  tempEl.style.lineHeight = getComputedStyle(textRef.value).lineHeight;
  tempEl.style.whiteSpace = "pre-wrap";
  tempEl.style.wordBreak = "break-word";

  document.body.appendChild(tempEl);

  while (true) {
    tempEl.textContent = truncated + "...展开";

    if (tempEl.scrollHeight <= parseFloat(maxHeight.value)) {
      // 找到了合适的截断点
      displayContent.value = truncated;
      break;
    }

    if (truncated.length <= 1) {
      // 文本太短，无法截断
      break;
    }

    // 逐步减少文本长度
    truncated = truncated.slice(0, -1);
  }

  document.body.removeChild(tempEl);
};

const toggleExpand = () => {
  isExpanded.value = !isExpanded.value;

  // 强制重绘
  if (textRef.value) {
    textRef.value.style.display = "none";
    nextTick(() => {
      textRef.value.style.display = "";
      emits(isExpanded.value ? "expand" : "collapse");
    });
  } else {
    emits(isExpanded.value ? "expand" : "collapse");
  }
};
</script>

<style scoped>
.text-content {
  overflow: hidden;
  position: relative;
  transition: max-height 0.3s ease;
}

.toggle-btn {
  display: inline;
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
  font-size: inherit;
  color: #165dff;
  white-space: nowrap;
}
</style>
